/**
 * Created Date: Friday, April 7th 2023, 12:40:46 am
 * Author: CodingGorit
 * -----
 * Last Modified: Fri Apr 21 2023
 * Modified By: CodingGorit
 * -----
 * Copyright © 2019 —— 2023 fmin-courses All Rights Reserved
 * ------------------------------------
 * Javascript will save your soul!
 */

import axios, { AxiosRequestConfig, AxiosResponse, AxiosInstance, AxiosRequestHeaders, AxiosError } from 'axios';
import { URL, TIMEOUT } from "@/Const";
import { useRouter } from 'vue-router';
import { message } from 'ant-design-vue';
import { IResult } from '@/types';

const router = useRouter();

export type Result<T> = {
    code: number,
    msg: string,
    data: T
}

interface AdaptAxiosRequestConfig extends AxiosRequestConfig {
    headers: AxiosRequestHeaders
  }

/**
 * @usage
 * import Request from './Request';
 * const request = Request.getInstance();
 * 
 * request.get(...);
 */
class Request {
    private static instance: Request;
    private _instance: AxiosInstance;
    private _baseConfig: AxiosRequestConfig = {
        baseURL: URL,
        timeout: TIMEOUT,
    }

    static getInstance() {
        if (!this.instance) {
            this.instance = new Request();
        }
        return this.instance;
    }

    constructor(config?: AxiosRequestConfig) {
        this._instance = axios.create(Object.assign(this._baseConfig, config));
        this._initInterceptors();
    }

    private _initInterceptors() {
        if (!this._instance) {
            return;
        }

        this._instance.interceptors.request.use(
            (config): AdaptAxiosRequestConfig => {
                // https://forum.vuejs.org/t/adding-x-frame-options-response-header-to-every-page-route-in-my-vue-app/31271/6
                // config.headers['X-Frame-Options'] = '*'
                const token = "111";
                if (token) {
                    config.headers.Authorization = "aaa";
                    (<{ Authorization: string }><object>config.headers).Authorization = token;
                    (config.headers as unknown as { Authorization: string }).Authorization = token;
                }
                return config;
            }, (error: AxiosError) => {
                // 对请求错误做些什么
                return Promise.reject(error);
            });

        // 添加响应拦截器
        this._instance.interceptors.response.use((response: AxiosResponse) => {
            // 对响应数据做点什么
            if (response.data.code === 50005) {
                // todo
            }
            return response;
        }, (error: AxiosError) => {
            // 对响应错误做点什么
            if (error && error.response) {
                // 1.公共错误处理
                // 2.根据响应码具体处理
                switch (error.response.status) {
                    case 400:
                        error.message = '错误请求'
                        break;
                    case 401:
                        error.message = '未授权，请重新登录'
                        break;
                    case 403:
                        error.message = '拒绝访问'
                        break;
                    case 404:
                        error.message = '请求错误,未找到该资源';
                        router.push("/404");
                        break;
                    case 405:
                        error.message = '请求方法未允许'
                        break;
                    case 408:
                        error.message = '请求超时'
                        break;
                    case 500:
                        const responseData = error.response.data as IResult;
                        if (responseData.code > 0) {
                            error.message = responseData.msg;
                        } else {
                            error.message = '服务器端出错'
                        }
                        break;
                    case 501:
                        error.message = '网络未实现'
                        break;
                    case 502:
                        error.message = '网络错误'
                        break;
                    case 503:
                        error.message = '服务不可用'
                        break;
                    case 504:
                        error.message = '网络超时'
                        break;
                    case 505:
                        error.message = 'http版本不支持该请求'
                        break;
                    default:
                        error.message = `连接错误${error.response.status}`
                }
            } else {
                // 超时处理
                if (JSON.stringify(error).includes('timeout')) {
                    message.error('服务器响应超时，请刷新当前页')
                }
                error.message = '连接服务器失败'
            }
            return Promise.reject(error);
        });
    }

    // 类型参数的作用，T决定AxiosResponse实例中data的类型
	public request<T = any>(config: AxiosRequestConfig): Promise<T> {
		return new Promise((resolve, reject) => {
			this._instance
				.request<any, Result<T>>(config)
				.then((res) => {
					resolve(res.data);
				})
				.catch((err) => {
					reject(err);
				});
		});
	}

    public get<T = any>(config: AxiosRequestConfig): Promise<T> {
        return this.request({ ...config, method: 'GET' });
    }

    public post<T = any>(config: AxiosRequestConfig): Promise<T> {
        return this.request({ ...config, method: 'POST' });
    }

    public delete<T = any>(config: AxiosRequestConfig): Promise<T> {
        return this.request({ ...config, method: 'DELETE' });
    }

    public patch<T = any>(config: AxiosRequestConfig): Promise<T> {
        return this.request({ ...config, method: 'PATCH' });
    }

    public put<T = any>(config: AxiosRequestConfig): Promise<T> {
        return this.request({ ...config, method: 'PUT' });
    }
}


export default Request;
